package lambda;

// *** 函数式接口 ***
// 要了解Lambda表达式,首先需要了解什么是函数式接口，函数式接口定义：一个接口有且只有一个抽象方法 。
//注意：
//1. 如果一个接口只有一个抽象方法，那么该接口就是一个函数式接口
//2. 如果我们在某个接口上声明了 @FunctionalInterface 注解，那么编译器就会按照函数式接口的定义来要求该接
//口，这样如果有两个抽象方法，程序编译就会报错的。所以，从某种意义上来说，只要你保证你的接口中只
//有一个抽象方法，你可以不加这个注解。加上就会自动进行检测的。
//定义方式：

import javax.xml.soap.SAAJMetaFactory;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

////无返回值无参数
@FunctionalInterface
interface NoParameterNoReturn {
    //注意：只能有一个方法
    void test(); // 只有一个抽象方法
    //void test2(); // 不能有两个抽象方法

    // 但是可以这样加
    default void test2() {
        System.out.println("JDK1.8新特性，default默认方法可以有具体的实现");
    }

    static void test3() {
        System.out.println("JDK1.8新特性，default默认方法可以有具体的实现");
    }
}

//无返回值一个参数
@FunctionalInterface
interface OneParameterNoReturn {
    void test(int a);
}
//无返回值多个参数
@FunctionalInterface
interface MoreParameterNoReturn {
    void test(int a,int b);
}
//有返回值无参数
@FunctionalInterface
interface NoParameterReturn {
    int test();
}

//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
    int test(int a);
}
//有返回值多参数
@FunctionalInterface
interface MoreParameterReturn {
    int test(int a,int b);
}

public class Lambda {
    public static void main(String[] args) {
        // Map类接口也可以用foreach语句遍历
        Map<String,Integer> map = new HashMap<>();
        map.put("hello",3);
        map.put("abc",2);
        map.put("this",5);
        map.forEach(new BiConsumer<String, Integer>() {
            @Override
            public void accept(String s, Integer integer) {
                System.out.println("key："+s+"val: "+integer);
            }
        });
        System.out.println("===============");
        // 接下来就变成lambda表达式的形式
        // 进行遍历输出
        map.forEach((String s, Integer integer) -> System.out.println("key："+s+"val: "+integer));

        //5 总结
        //Lambda表达式的优点很明显，在代码层次上来说，使代码变得非常的简洁。缺点也很明显，代码不易读。
        //优点：
        //1. 代码简洁，开发迅速
        //2. 方便函数式编程
        //3. 非常容易进行并行计算
        //4. Java 引入 Lambda，改善了集合操作
        //缺点：
        //1. 代码可读性变差
        //2. 在非并行计算中，很多计算未必有传统的 for 性能要高
        //3. 不容易进行调试
    }
    public static void main3(String[] args) {
        // 4 Lambda在集合当中的使用
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("this");
        list.add("day");
        // 接下来遍历集合并打印
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                // 需要重写里面的accept方法
                System.out.println(s);
            }
        });
        System.out.println("===============");
        // 接下来用lambda表达式来写
        list.forEach(s -> System.out.println(s));
        // 接下来再写一个排序方法
        list.sort(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        });
        list.forEach(s -> System.out.println(s));
        System.out.println("====================");
        //  // 接下来用lambda表达式来写
        list.sort(((o1, o2) -> {return o1.compareTo(o2);}));
        list.forEach(s -> System.out.println(s));



    }
    public static void main2(String[] args) {
        // 3 变量捕获
        // Lambda 表达式中存在变量捕获 ，了解了变量捕获之后，我们才能更好的理解Lambda 表达式的作用域 。Java当
        //中的匿名类中，会存在变量捕获。

        int a = 10;
        NoParameterNoReturn noParameterNoReturn1 = new NoParameterNoReturn(){
            @Override
            public void test() {
                //a = 99; //在这里面更改是错误的
                System.out.println("hello->"+a);  // 这里面的a就是我们捕获的变量
                // 但是是由条件的
                // 这个a是一个常量，或者是一个没有改变过值的变量！
            }
        };
        noParameterNoReturn1.test();

        // 同理 lambda 语句的变量捕获也是一样的道理
        int b = 10;
        NoParameterNoReturn noParameterNoReturn = ()->{
            //b =100; 在这里面修改是错误的
            System.out.println("无参数无返回值->"+b);
        };
        noParameterNoReturn.test();

    }
    // ****Lambda 表达式****

    //1.1 Lambda表达式的语法
    //基本语法: (parameters) -> expression 或 (parameters) ->{ statements; }
    //Lambda表达式由三部分组成：
    //1. paramaters：类似方法中的形参列表，这里的参数是函数式接口里的参数。这里的参数类型可以明确的声明
    //也可不声明而由JVM隐含的推断。另外当只有一个推断类型时可以省略掉圆括号。
    //2. ->：可理解为“被用于”的意思
    //3. 方法体：可以是表达式也可以代码块，是函数式接口里方法的实现。代码块可返回一个值或者什么都不反
    //回，这里的代码块块等同于方法的方法体。如果是表达式，也可以返回一个值或者什么都不反回。

    // 1. 不需要参数,返回值为 2
    //() -> 2

    // 2. 接收一个参数(数字类型),返回其2倍的值
    //x -> 2 * x

    // 3. 接受2个参数(数字),并返回他们的和
    //(x, y) -> x + y

    // 4. 接收2个int型整数,返回他们的乘积
    //(int x, int y) -> x * y

    // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
    //(String s) -> System.out.print(s)



    public static void main1(String[] args) {
        NoParameterNoReturn noParameterNoReturn1 = new NoParameterNoReturn(){
            @Override
            public void test() {
                System.out.println("hello"); // 这个是一个类实现了这个接口并且重写了这个方法
            }
        };

        noParameterNoReturn1.test();

        // ，Lambda可以理解为：Lambda就是匿名内部类的简化，实际上是创建了一个类，实现了接
        //口，重写了接口的方法 。 优点是节省代码量

        // 下面用lambda表达式来写
        NoParameterNoReturn noParameterNoReturn = ()->{
            System.out.println("无参数无返回值");
        };
        noParameterNoReturn.test();

        // 参数的类型只有一个的时候可以省略 也可以省略参数的括号
        OneParameterNoReturn oneParameterNoReturn = (int a)->{
            System.out.println("一个参数无返回值："+ a);
        };
        oneParameterNoReturn.test(10);

        // 多个参数的时候 类型不可以单个省略 除非一起省略，括号不能省略
        MoreParameterNoReturn moreParameterNoReturn = (int a,int b)->{
            System.out.println("多个参数无返回值："+a+" "+b);
        };
        moreParameterNoReturn.test(20,30);

        // 可以省略return
        NoParameterReturn noParameterReturn = ()->{
            System.out.println("有返回值无参数！");
            return 40;
        };
        //接收函数的返回值
        int ret = noParameterReturn.test();
        System.out.println(ret);

        // 这个return 也可以省略
        OneParameterReturn oneParameterReturn = (int a)->{
            System.out.println("有返回值有一个参数！");
            return a;
        };
        ret = oneParameterReturn.test(50);
        System.out.println(ret);



        MoreParameterReturn moreParameterReturn = (int a,int b)->{
            System.out.println("有返回值多个参数！");
            return a+b;
        };
        ret = moreParameterReturn.test(60,70);
        System.out.println(ret);
    }

    // 2.1 语法精简
    //1. 参数类型可以省略，如果需要省略，每个参数的类型都要省略。
    //2. 参数的小括号里面只有一个参数，那么小括号可以省略
    //3. 如果方法体当中只有一句代码，那么大括号可以省略
    //4. 如果方法体中只有一条语句，且是return语句，那么大括号可以省略，且去掉return关键字。



}
